package model.primitive;

import java.util.ArrayList;
import java.util.List;

import javax.vecmath.Point2f;
import javax.vecmath.Point3d; 
import javax.vecmath.Vector3d;

import model.ray.Ray;
import model.shader.Shader;

public class Sphere extends Primitive {
	private Point3d coordinates;
	private Double radius;
	
	private Vector3d vn; // center to north
	private Vector3d ve; 
	private Vector3d vc;
	
	
	public Sphere(Shader shader, String name) {
		super(shader, name);
	}
	
	public Sphere(Shader shader, String name, Point3d coordinates, double radius) {
		super(shader, name);
		this.coordinates = coordinates;
		this.radius = radius;
		this.vn = new Vector3d(0,1,0);
		this.ve = new Vector3d(1,0,0);
		this.vc = new Vector3d();
		this.vc.cross(vn,ve);
	}
	
	
	public Point3d getCenter() {
		return coordinates;
	}

	public void setC(Point3d c) {
		this.coordinates = c;
	}

	public Double getRadius() {
		return radius;
	}
	public void setR(Double r) {
		this.radius = r;
	}
	
	@Override
    public List<Point3d> getIntersections(Ray ray) {
		
		List<Point3d> intersections = new ArrayList<Point3d>();
		
        double Xc= this.coordinates.x;
        double Yc= this.coordinates.y;
        double Zc= this.coordinates.z;
        double Xo= ray.getOrigin().x;
        double Yo= ray.getOrigin().y;
        double Zo= ray.getOrigin().z;
        double Xd= ray.getDirection().x;
        double Yd= ray.getDirection().y;
        double Zd= ray.getDirection().z;
        
        double B = 2 * (Xd*(Xo-Xc)+Yd*(Yo-Yc)+Zd*(Zo-Zc));
        double C = Math.pow(Xo-Xc, 2) + Math.pow(Yo-Yc, 2)+ Math.pow(Zo-Zc, 2) - Math.pow(this.radius,2);
        
        if( Math.pow(B, 2)-4*C < 0 )
            return null;
        if( Math.pow(B, 2)-4*C == 0) {
        	intersections.add(ray.getPoint(-B/2));
        	return intersections;
        }
        
        double to=(-B-Math.sqrt(B*B-4*C))/2; 
        double t1=(-B+Math.sqrt(B*B-4*C))/2;
        
        // la interseccion del origen del rayo hacia atras...
        // por ende no hay interseccion
        if( to < 0 && t1 < 0 )
            return null;
        
        if (to < 0) {
        	if (t1 < 0) {
        		return null; // object behind ray, no intersection
        	} else {
        		to = t1; // intersection is on ray(t1)
        	}
        } else {
        	// intersection is first at t1, swap values
        	if (t1 > to) {
        		double swap = t1;
            	t1 = to;
            	to = swap;
        	}
        }
            // el origen del rayo puede estar dentro de la esfera;
            ;
        // reemplazo con el t menor en la ecuación del rayo para saber cual es el mas
        // cercano al origen y ya , eso debería estar en otro metodo.
        
        intersections.add(ray.getPoint(to));
        intersections.add(ray.getPoint(t1));
        
        return intersections;
	}
	
    @Override
    public String toString() {
    	StringBuffer sb = new StringBuffer();
		sb.append("Sphere\n");
		sb.append("name: "+ getName()).append("\n");
		sb.append("c: " + this.coordinates).append("\n");
		sb.append("r: " + this.radius).append("\n");
		return sb.toString();
    }

	@Override
	public void translate(Point3d pos) {
		if( pos !=null )
			this.coordinates.add(pos);
		
	}

	@Override
	public Vector3d getNormal(Point3d pos) {
		Vector3d u = new Vector3d();
		u.sub(pos, this.coordinates);
		u.scale(1.0/this.radius);
		return u;
	}

	@Override
	public void rotatex(Double degree) {
		// Do nothing !
	}

	@Override
	public void rotatey(Double degree) {
		// Do nothing !
	}

	@Override
	public void rotatez(Double degree) {
		// Do nothing 
	}

	@Override
	public void scalex(Double sx) {
		// Do nothing !
	}

	@Override
	public void scaley(Double sy) {
		// Do nothing !
	}

	@Override
	public void scalez(Double sz) {
		// Do nothing !
	}

	@Override
	public void scaleu(Double su) {
		if (su!=null && su>0)
			this.radius*=su;
	}

	@Override
	public Point2f getUV(Point3d p) {

		Vector3d hit = new Vector3d(p);
		
		float u= (float) (p.x/hit.length());
		float v= (float) (p.y/hit.length());
		
		return new Point2f (u,v); 
	}
	
	

}
